﻿namespace FitnessTracker.Observables
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    using FitnessTracker.Packet;

    public class HxMReader : IObservable<HxMPacket>, IObserver<byte[]>
    {
        private static HxMReader uniqueInstanceOfHxMReader;
        private readonly List<IObserver<HxMPacket>> hxMObservers = new List<IObserver<HxMPacket>>();
        
        private HxMReader()
        {
        }

        private readonly object synchronizer = new object();
        
        public double LastDistanceValueReceived { get; private set; }

        public byte LastStridesValueReceived { get; private set; }

        public static HxMReader GetInstance()
        {
            return uniqueInstanceOfHxMReader ?? (uniqueInstanceOfHxMReader = new HxMReader());
        }

        public static bool IsBufferValid(byte[] bufferStore)
        {
            return HxMPacketStructureValidator.IsStructureValid(bufferStore) &&
                   HxMPacketIntegrityValidator.IsCRCValid(bufferStore) &&
                   HxMPacketValueRangeValidator.AreDataPointValuesInRange(bufferStore);
        }

        public IDisposable Subscribe(IObserver<HxMPacket> observer)
        {
            Debug.WriteLine("HxMReader received subscription request from " + observer);
            if (!this.hxMObservers.Contains(observer))
            {
                lock (this.synchronizer) { this.hxMObservers.Add(observer);}
                Debug.WriteLine("HxMReader added new observer " + observer.GetType());
                Debug.WriteLine("New list of observers");
                foreach (var hxMObserver in hxMObservers)
                {
                    Debug.WriteLine(hxMObserver.GetType());
                }
            }

            return new Unsubscriber<HxMPacket>(this.hxMObservers, observer);
        }

        public void OnNext(byte[] value)
        {
            if (!IsBufferValid(value))
            {
                throw new Exception(
                   string.Format(
                   "HxMReader Received a packet which failed validation. Raw packet {0}",
                   value));
            }

            var packet = new HxMPacket(value);
            this.UpdateLastReceivedValues(packet);
            this.NotifyObservers(packet);
        }

        public void OnError(Exception error)
        {
            throw error;
        }

        public void OnCompleted()
        {
            throw new NotImplementedException();
        }

        private void NotifyObservers(HxMPacket packet)
        {
            lock (this.synchronizer)
            {
                foreach (var hxMObserver in hxMObservers)
                {
                    Debug.WriteLine("About to send notifications to " + hxMObserver.GetType());
                }

                foreach (var hxMObserver in this.hxMObservers)
                {
                    hxMObserver.OnNext(packet);
                    Debug.WriteLine("HxMReader notified " + hxMObserver.GetType());
                }
            }
            
        }

        private void UpdateLastReceivedValues(HxMPacket packet)
        {
            this.LastDistanceValueReceived = packet.Payload.DistanceInMeters;
            this.LastStridesValueReceived = packet.Payload.Strides;
        }
    }
}
